package com.amar.dtadmin.apidoc.util;

import com.amar.dtadmin.apidoc.constant.TypeEnum;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiArrayType;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiType;
import com.intellij.psi.javadoc.PsiDocComment;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xiaoshiqiang
 * @since 2025/2/11
 */
public class PsiTypeUtil {
    // 定义整型基本数据类型及其包装类的全限定名列表
    private static final List<String> INTEGER_TYPE_NAMES = Arrays.asList(
            "int",
            "java.lang.Integer",
            "long",
            "java.lang.Long",
            "short",
            "java.lang.Short",
            "byte",
            "java.lang.Byte"
    );

    // 定义浮点类型（包括基本类型、包装类和 BigDecimal）的全限定名列表
    private static final List<String> FLOATING_POINT_TYPE_NAMES = Arrays.asList(
            "float",
            "java.lang.Float",
            "double",
            "java.lang.Double",
            "java.math.BigDecimal"
    );

    // 定义常见日期类型的全限定名列表
    private static final List<String> DATE_TYPE_NAMES = Arrays.asList(
            "java.util.Date",
            "java.sql.Date",
            "java.time.LocalDate",
            "java.time.LocalDateTime",
            "java.time.ZonedDateTime",
            "java.time.OffsetDateTime",
            "java.time.Instant"
    );

    // 定义 javax.validation 包下表示非空、非 null 的注解全限定名列表
    private static final List<String> NON_NULL_ANNOTATIONS = Arrays.asList(
            "javax.validation.constraints.NotNull",
            "javax.validation.constraints.NotEmpty",
            "javax.validation.constraints.NotBlank"
    );

    public static FieldInfo getFieldInfo(PsiField field) {
        PsiType psiType = field.getType();
        String fieldName = field.getName();
        String fieldDoc = getFieldDocument(field);
        String isRequired = isRequireField(field);
        if (isJavaBean(psiType)) {
            PsiClass psiClass = ((PsiClassType) psiType).resolve();
            return new FieldInfo(TypeEnum.JSONOBJECT.getType(), psiClass, fieldName, fieldDoc, isRequired);
        }
        if (isMap(psiType)) {
            return new FieldInfo(TypeEnum.JSONOBJECT.getType(), null, fieldName, fieldDoc, isRequired);
        }
        if (isStringType(psiType) || isDateType(psiType)) {
            return new FieldInfo(TypeEnum.VARCHAR.getType(), null, fieldName, fieldDoc, isRequired);
        }
        if (isInteger(psiType)) {
            return new FieldInfo(TypeEnum.NUMBER.getType(), null, fieldName, fieldDoc, isRequired);
        }
        if (isDecimal(psiType)) {
            return new FieldInfo(TypeEnum.DECIMAL.getType(), null, fieldName, fieldDoc, isRequired);
        }
        if (isBoolean(psiType)) {
            return new FieldInfo(TypeEnum.BOOLEAN.getType(), null, fieldName, fieldDoc, isRequired);
        }
        if (isJavaBeanArray(psiType)) {
            PsiClass psiClass = ((PsiClassType) ((PsiArrayType) psiType).getComponentType()).resolve();
            return new FieldInfo(TypeEnum.JSONARRAY.getType(), psiClass, fieldName, fieldDoc, isRequired);
        }
        if (isJavaBeanCollection(psiType)) {
            PsiClass psiClass = getJavaBeanCollectionClass(psiType);
            return new FieldInfo(TypeEnum.JSONARRAY.getType(), psiClass, fieldName, fieldDoc, isRequired);
        }
        if (isCollection(psiType) && !isJavaBeanCollection(psiType)) {
            return new FieldInfo(TypeEnum.ARRAY.getType(), null, fieldName, fieldDoc, isRequired);
        }
        if (isArrayType(psiType) && !isJavaBeanArray(psiType)) {
            return new FieldInfo(TypeEnum.ARRAY.getType(), null, fieldName, fieldDoc, isRequired);
        }
        return null;
    }

    private static String getFieldDocument(PsiField field) {
        // 先尝试获取字段的文档注释
        PsiDocComment docComment = field.getDocComment();
        if (docComment != null) {
            return removeCommentSymbols(docComment.getText());
        }
        // 检查字段前面紧邻的元素是否为注释
        PsiElement prevSibling = field.getPrevSibling();
        if (prevSibling instanceof PsiComment comment) {
            return removeCommentSymbols(comment.getText());
        }
        // 如果既没有文档注释，前面紧邻的元素也不是注释，则返回空字符串
        return "";
    }

    private static String removeCommentSymbols(String comment) {
        // 去除文档注释符号
        comment = comment.replaceAll("^/\\*\\*", "").replaceAll("\\*/$", "").replaceAll("^\\s*\\*\\s*", "");
        // 去除行注释符号
        comment = comment.replaceAll("^//\\s*", "");
        // 去除首尾空白字符
        return comment.trim();
    }

    private static String isRequireField(PsiField psiField) {
        if (psiField == null) {
            return "N";
        }
        PsiModifierList modifierList = psiField.getModifierList();
        if (modifierList != null) {
            PsiAnnotation[] annotations = modifierList.getAnnotations();
            for (PsiAnnotation annotation : annotations) {
                String qualifiedName = annotation.getQualifiedName();
                if (qualifiedName != null && NON_NULL_ANNOTATIONS.contains(qualifiedName)) {
                    return "Y";
                }
            }
        }
        return "N";
    }

    /**
     * 判断 PsiType 是否为数组类型
     *
     * @param psiType 要判断的 PsiType 对象
     * @return 如果是数组类型返回 true，否则返回 false
     */
    public static boolean isArrayType(PsiType psiType) {
        return psiType instanceof PsiArrayType;
    }

    /**
     * 判断 PsiClass 是否为 JavaBean
     *
     * @param psiClass 要检查的 PsiClass
     * @return 如果是 JavaBean 返回 true，否则返回 false
     */
    public static boolean isJavaBean(PsiClass psiClass) {
        // 检查是否有公共的无参构造函数
        if (!hasPublicNoArgConstructor(psiClass)) {
            return false;
        }
        // 获取类的所有字段
        PsiField[] fields = psiClass.getFields();
        if (fields.length == 0) {
            return true;
        }
        // 存储字段名和对应的 getter、setter 方法是否存在的映射
        Map<String, Boolean[]> fieldAccessMethods = new HashMap<>();
        for (PsiField field : fields) {
            fieldAccessMethods.put(field.getName(), new Boolean[]{false, false});
        }
        // 获取类的所有方法
        PsiMethod[] methods = psiClass.getMethods();
        for (PsiMethod method : methods) {
            String methodName = method.getName();
            if (methodName.startsWith("get") || methodName.startsWith("is")) {
                // 尝试获取对应的字段名
                String fieldName = getFieldNameFromAccessor(methodName);
                if (fieldAccessMethods.containsKey(fieldName)) {
                    fieldAccessMethods.get(fieldName)[0] = true;
                }
            } else if (methodName.startsWith("set")) {
                // 尝试获取对应的字段名
                String fieldName = getFieldNameFromAccessor(methodName);
                if (fieldAccessMethods.containsKey(fieldName)) {
                    fieldAccessMethods.get(fieldName)[1] = true;
                }
            }
        }
        // 检查是否存在字段都有对应的 getter 和 setter 方法
        for (Boolean[] accessMethods : fieldAccessMethods.values()) {
            if (accessMethods[0] && accessMethods[1]) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断 PsiType 是否为 JavaBean
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是 JavaBean 返回 true，否则返回 false
     */
    public static boolean isJavaBean(PsiType psiType) {
        if (psiType instanceof PsiClassType) {
            PsiClass psiClass = ((PsiClassType) psiType).resolve();
            return isJavaBean(psiClass);
        }
        return false;
    }

    /**
     * 判断 PsiType 是否为 JavaBean 集合
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是 JavaBean 集合返回 true，否则返回 false
     */
    public static boolean isJavaBeanCollection(PsiType psiType) {
        if (psiType instanceof PsiClassType) {
            PsiClass psiClass = ((PsiClassType) psiType).resolve();
            if (psiClass != null && isCollectionClass(psiClass)) {
                PsiType[] typeParameters = ((PsiClassType) psiType).getParameters();
                if (typeParameters.length > 0) {
                    return isJavaBean(typeParameters[0]);
                }
            }
        }
        return false;
    }

    public static PsiClass getJavaBeanCollectionClass(PsiType psiType) {
        PsiClass psiClass = ((PsiClassType) psiType).resolve();
        return ((PsiClassType) ((PsiClassType) psiType).getParameters()[0]).resolve();
    }

    /**
     * 判断 PsiType 是否为 JavaBean 集合
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是 JavaBean 集合返回 true，否则返回 false
     */
    public static boolean isMap(PsiType psiType) {
        if (psiType instanceof PsiClassType) {
            PsiClass psiClass = ((PsiClassType) psiType).resolve();
            return psiClass != null && isMapClass(psiClass);
        }
        return false;
    }

    /**
     * 判断 PsiType 是否为 JavaBean 数组
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是 JavaBean 数组返回 true，否则返回 false
     */
    public static boolean isJavaBeanArray(PsiType psiType) {
        if (psiType instanceof PsiArrayType) {
            PsiType componentType = ((PsiArrayType) psiType).getComponentType();
            return isJavaBean(componentType);
        }
        return false;
    }

    /**
     * 检查类是否为集合类
     *
     * @param psiClass 要检查的 PsiClass
     * @return 如果是集合类返回 true，否则返回 false
     */
    public static boolean isCollectionClass(PsiClass psiClass) {
        PsiClass[] supers = psiClass.getSupers();
        for (PsiClass superClass : supers) {
            if ("java.util.Collection".equals(superClass.getQualifiedName())) {
                return true;
            }
            if (isCollectionClass(superClass)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isCollection(PsiType psiType) {
        if (psiType instanceof PsiClassType classType) {
            PsiClass psiClass = classType.resolve();
            return psiClass != null && isCollectionClass(psiClass);
        }
        return false;
    }

    /**
     * 检查类是否为Map类
     *
     * @param psiClass 要检查的 PsiClass
     * @return 如果是集合类返回 true，否则返回 false
     */
    public static boolean isMapClass(PsiClass psiClass) {
        PsiClass[] supers = psiClass.getSupers();
        for (PsiClass superClass : supers) {
            if ("java.util.Map".equals(superClass.getQualifiedName())) {
                return true;
            }
            if (isCollectionClass(superClass)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断 PsiType 是否为整型（包括基本类型和包装类）
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是整型返回 true，否则返回 false
     */
    public static boolean isInteger(PsiType psiType) {
        if (psiType == null) {
            return false;
        }
        String typeName = psiType.getCanonicalText();
        return INTEGER_TYPE_NAMES.contains(typeName);
    }

    /**
     * 判断 PsiType 是否为布尔类型
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是整型返回 true，否则返回 false
     */
    public static boolean isBoolean(PsiType psiType) {
        if (psiType == null) {
            return false;
        }
        String typeName = psiType.getCanonicalText();
        return "boolean".equals(typeName) || "java.lang.Boolean".equals(typeName);
    }

    /**
     * 判断 PsiType 是否为浮点类型（包括基本类型、包装类和 BigDecimal）
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是浮点类型返回 true，否则返回 false
     */
    public static boolean isDecimal(PsiType psiType) {
        if (psiType == null) {
            return false;
        }
        String typeName = psiType.getCanonicalText();
        return FLOATING_POINT_TYPE_NAMES.contains(typeName);
    }

    /**
     * 判断 PsiType 是否为日期类型
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是日期类型返回 true，否则返回 false
     */
    public static boolean isDateType(PsiType psiType) {
        if (psiType == null) {
            return false;
        }
        String typeName = psiType.getCanonicalText();
        return DATE_TYPE_NAMES.contains(typeName);
    }

    /**
     * 判断 PsiType 是否为字符串类型
     *
     * @param psiType 要检查的 PsiType
     * @return 如果是字符串类型返回 true，否则返回 false
     */
    public static boolean isStringType(PsiType psiType) {
        if (psiType == null) {
            return false;
        }
        // 获取 PsiType 的全限定名
        String typeName = psiType.getCanonicalText();
        // 检查全限定名是否为字符串类型的全限定名
        return "java.lang.String".equals(typeName);
    }

    /**
     * 检查类是否有公共的无参构造函数
     *
     * @param psiClass 要检查的 PsiClass
     * @return 如果有公共的无参构造函数返回 true，否则返回 false
     */
    private static boolean hasPublicNoArgConstructor(PsiClass psiClass) {
        PsiMethod[] constructors = psiClass.getConstructors();
        for (PsiMethod constructor : constructors) {
            if (constructor.getParameterList().getParametersCount() == 0 && constructor.hasModifierProperty(PsiModifier.PUBLIC)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 从 getter 或 setter 方法名中提取对应的字段名
     *
     * @param methodName 方法名
     * @return 对应的字段名
     */
    public static String getFieldNameFromAccessor(String methodName) {
        String prefix = methodName.startsWith("is") ? "is" : methodName.startsWith("get") ? "get" : "set";
        String fieldName = methodName.substring(prefix.length());
        if (!fieldName.isEmpty()) {
            fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
        }
        return fieldName;
    }

    @Data
    @AllArgsConstructor
    public static class FieldInfo {
        private String fieldType;
        private PsiClass typeClass;
        private String fieldName;
        private String fieldDocument;
        private String isRequired;
    }


}
